import akshare as ak
import pandas as pd
import numpy as np
import talib
import time
import configparser
import logging
from typing import List, Tuple, Dict
import signal
import requests
from bs4 import BeautifulSoup

# 读取配置文件
config = configparser.ConfigParser()
config.read('stock_analysis_config.ini')

# 从配置文件获取参数
SHORT_MA_PERIOD = config.getint('Indicators', 'SHORT_MA_PERIOD')
LONG_MA_PERIOD = config.getint('Indicators', 'LONG_MA_PERIOD')
RSI_PERIOD = config.getint('Indicators', 'RSI_PERIOD')
OVERSOLD_THRESHOLD = config.getfloat('Indicators', 'OVERSOLD_THRESHOLD')
MACD_FAST_PERIOD = config.getint('Indicators', 'MACD_FAST_PERIOD')
MACD_SLOW_PERIOD = config.getint('Indicators', 'MACD_SLOW_PERIOD')
MACD_SIGNAL_PERIOD = config.getint('Indicators', 'MACD_SIGNAL_PERIOD')
BOLLINGER_N_PERIOD = config.getint('Indicators', 'BOLLINGER_N_PERIOD')
BOLLINGER_K = config.getfloat('Indicators', 'BOLLINGER_K')
KDJ_WINDOW = config.getint('Indicators', 'KDJ_WINDOW')
LIMIT_UP_THRESHOLD = config.getfloat('Indicators', 'LIMIT_UP_THRESHOLD')
HISTORY_DAYS = config.getint('Indicators', 'HISTORY_DAYS')

# 配置日志
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s',
                    handlers=[logging.FileHandler('stock_analysis.log'), logging.StreamHandler()])


class TechnicalIndicators:
    @staticmethod
    def calculate_moving_averages(data: pd.DataFrame, short_period: int, long_period: int) -> pd.DataFrame:
        data['sma_short'] = data['close'].rolling(short_period).mean()
        data['sma_long'] = data['close'].rolling(long_period).mean()
        return data

    @staticmethod
    def detect_rsi_oversold(data: pd.DataFrame, rsi_period: int, oversold_threshold: float) -> bool:
        rsi = talib.RSI(data['close'].values, timeperiod=rsi_period)
        return rsi[-1] < oversold_threshold

    @staticmethod
    def detect_macd_crossover(data: pd.DataFrame, fast_period: int, slow_period: int, signal_period: int) -> bool:
        macd, signal, _ = talib.MACD(data['close'].values, fastperiod=fast_period,
                                     slowperiod=slow_period, signalperiod=signal_period)
        return macd[-1] > signal[-1] and macd[-2] <= signal[-2]

    @staticmethod
    def analyze_volume_change(data: pd.DataFrame) -> str:
        volume_change = data['volume'].pct_change()
        if volume_change.empty:
            return "成交量数据不可用"
        last_volume_change = volume_change.iloc[-1]
        if last_volume_change > 0.3:
            return "成交量显著增加"
        elif last_volume_change < -0.3:
            return "成交量显著减少"
        return "成交量相对稳定"

    @staticmethod
    def calculate_price_volatility(data: pd.DataFrame) -> float:
        returns = data['close'].pct_change()
        return returns.std()

    @staticmethod
    def calculate_bollinger_bands(data: pd.DataFrame, n_period: int, k: float) -> pd.DataFrame:
        upper, middle, lower = talib.BBANDS(data['close'].values, timeperiod=n_period,
                                            nbdevup=k, nbdevdn=k)
        data['bollinger_upper'] = upper
        data['bollinger_middle'] = middle
        data['bollinger_lower'] = lower
        return data

    @staticmethod
    def calculate_kdj(data: pd.DataFrame, window: int) -> pd.DataFrame:
        low_prices = data['low'].rolling(window).min()
        high_prices = data['high'].rolling(window).max()
        close_prices = data['close']
        rsv = ((close_prices - low_prices) / (high_prices - low_prices)) * 100
        k = pd.Series(rsv).ewm(com=2).mean()
        d = k.ewm(com=2).mean()
        j = 3 * k - 2 * d
        data['k'] = k
        data['d'] = d
        data['j'] = j
        return data

    @staticmethod
    def detect_kdj_crossover(data: pd.DataFrame) -> bool:
        return data['k'].iloc[-1] > data['d'].iloc[-1] and data['k'].iloc[-2] <= data['d'].iloc[-2]

    # 获取主力资金流向数据（模拟从东方财富获取的思路，实际可能需要更复杂的处理）
    @staticmethod
    def calculate_main_funds_flow(symbol: str) -> pd.Series:
        try:
            stock_individual_fund_flow_df = ak.stock_individual_fund_flow(stock={symbol})
            return pd.Series(stock_individual_fund_flow_df)
        except Exception as e:
            logging.error(f"获取主力资金流向数据失败: {e}")
            return pd.Series()

    # 获取筹码分布数据（这里只是示例，实际需要与相应的数据提供商接口对接）
    @staticmethod
    def calculate_chip_concentration_90(symbol: str) -> float:
        # 假设通过某个第三方数据接口获取，这里返回一个示例值，实际需替换
        return 15.0

    # 获取获利盘比例数据（这里只是示例，实际需要与相应的数据提供商接口对接）
    @staticmethod
    def calculate_profit_plate_ratio(symbol: str) -> float:
        # 假设通过某个第三方数据接口获取，这里返回一个示例值，实际需替换
        return 70.0

    # 获取融资融券余额数据（这里只是示例，实际需要与相应的数据提供商接口对接）
    @staticmethod
    def calculate_financing_balance(symbol: str) -> float:
        # 假设通过证券公司 API 获取，这里返回一个示例值，实际需替换
        return 100000.0

    @staticmethod
    def calculate_short_selling_balance(symbol: str) -> float:
        # 假设通过证券公司 API 获取，这里返回一个示例值，实际需替换
        return 10000.0


def get_stock_daily_data(symbol: str, start_date: str, end_date: str) -> pd.DataFrame:
    def handler(signum, frame):
        raise TimeoutError("获取股票数据超时")

    signal.signal(signal.SIGALRM, handler)
    signal.alarm(10)
    try:
        logging.info(f"正在获取 {symbol} 的股票数据...")
        stock_data = ak.stock_zh_a_daily(symbol=symbol, start_date=start_date, end_date=end_date)
        logging.info(f"成功获取 {symbol} 的股票数据。")
        signal.alarm(0)
        return stock_data
    except TimeoutError:
        logging.warning(f"获取 {symbol} 股票数据超时，跳过。")
        return pd.DataFrame()
    except Exception as e:
        logging.error(f"获取 {symbol} 股票数据时出错: {e}")
        return pd.DataFrame()


def get_strategy_reasons(symbol: str, data: pd.DataFrame) -> List[str]:
    reasons = []
    # 移动平均线交叉
    if data['sma_short'].iloc[-1] > data['sma_long'].iloc[-1] and data['sma_short'].iloc[-2] <= data['sma_long'].iloc[-2]:
        reasons.append("短期均线向上穿过长期均线")
    else:
        reasons.append("短期均线未向上穿过长期均线")
    # RSI超卖
    if TechnicalIndicators.detect_rsi_oversold(data, RSI_PERIOD, OVERSOLD_THRESHOLD):
        reasons.append("RSI 超卖")
    else:
        reasons.append("RSI 未超卖")
    # MACD金叉
    if TechnicalIndicators.detect_macd_crossover(data, MACD_FAST_PERIOD, MACD_SLOW_PERIOD, MACD_SIGNAL_PERIOD):
        reasons.append("MACD 金叉")
    else:
        reasons.append("MACD 未金叉")
    # 成交量变化
    reasons.append(TechnicalIndicators.analyze_volume_change(data))
    # 价格波动率
    volatility = TechnicalIndicators.calculate_price_volatility(data)
    if volatility < 0.12:
        reasons.append("价格波动相对较低")
    else:
        reasons.append("价格波动相对较高")
    # 布林带策略
    close_price = data['close'].iloc[-1]
    upper_band = data['bollinger_upper'].iloc[-1]
    lower_band = data['bollinger_lower'].iloc[-1]
    if close_price > upper_band:
        reasons.append("股价突破布林带上轨")
    elif close_price < lower_band:
        reasons.append("股价跌破布林带下轨")
    else:
        reasons.append("股价在布林带内")
    # KDJ策略
    if TechnicalIndicators.detect_kdj_crossover(data):
        reasons.append("KDJ 金叉")
    else:
        reasons.append("KDJ 未金叉")

    # 新添加的指标分析逻辑
    main_funds_flow = TechnicalIndicators.calculate_main_funds_flow(symbol)
    if main_funds_flow.iloc[-1] > 0:
        reasons.append("主力资金净流入")
    else:
        reasons.append("主力资金净流出")

    chip_concentration_90 = TechnicalIndicators.calculate_chip_concentration_90(symbol)
    if chip_concentration_90 < 10:
        reasons.append("筹码高度集中（90%集中度低）")
    else:
        reasons.append("筹码相对分散（90%集中度高）")

    profit_plate_ratio = TechnicalIndicators.calculate_profit_plate_ratio(symbol)
    if profit_plate_ratio > 80:
        reasons.append("高获利盘比例")
    else:
        reasons.append("获利盘比例正常")

    financing_balance = TechnicalIndicators.calculate_financing_balance(symbol)
    short_selling_balance = TechnicalIndicators.calculate_short_selling_balance(symbol)
    if financing_balance > 0:
        reasons.append("融资余额增加，看多力量增强")
    if short_selling_balance > 0:
        reasons.append("融券余额增加，看空力量增强")

    return reasons


def has_limit_up_gene(data: pd.DataFrame) -> bool:
    limit_up_count = np.sum(data['close'].pct_change() >= LIMIT_UP_THRESHOLD)
    current_gain = data['close'].pct_change().iloc[-1]
    strong_gain_today = current_gain > 0.05
    technical_conditions = (TechnicalIndicators.detect_rsi_oversold(data, RSI_PERIOD, OVERSOLD_THRESHOLD) or
                            TechnicalIndicators.detect_macd_crossover(data, MACD_FAST_PERIOD, MACD_SLOW_PERIOD,
                                                                     MACD_SIGNAL_PERIOD)) and \
                           TechnicalIndicators.analyze_volume_change(data) == "成交量显著增加" and \
                           TechnicalIndicators.calculate_price_volatility(data) < 0.12 and \
                           TechnicalIndicators.detect_kdj_crossover(data)
    return limit_up_count > 0 or (strong_gain_today and technical_conditions)


def predict_buy_point(symbol: str) -> Tuple[str, List[str]]:
    today = pd.Timestamp.now().strftime('%Y-%m-%d')
    start_date = (pd.Timestamp(today) - pd.Timedelta(days=120)).strftime('%Y-%m-%d')
    stock_data = get_stock_daily_data(symbol, start_date, today)
    if stock_data.empty:
        return "数据获取失败", []
    stock_data['symbol'] = symbol
    stock_data = TechnicalIndicators.calculate_moving_averages(stock_data, SHORT_MA_PERIOD, LONG_MA_PERIOD)
    stock_data = TechnicalIndicators.calculate_bollinger_bands(stock_data, BOLLINGER_N_PERIOD, BOLLINGER_K)
    stock_data = TechnicalIndicators.calculate_kdj(stock_data, KDJ_WINDOW)
    reasons = get_strategy_reasons(symbol, stock_data)
    has_gene = has_limit_up_gene(stock_data)

    # 综合策略判断
    if (stock_data['sma_short'].iloc[-1] > stock_data['sma_long'].iloc[-1] and stock_data['sma_short'].iloc[-2] <=
            stock_data['sma_long'].iloc[-2]) and \
            (TechnicalIndicators.detect_rsi_oversold(stock_data, RSI_PERIOD, OVERSOLD_THRESHOLD) or
             TechnicalIndicators.detect_macd_crossover(stock_data, MACD_FAST_PERIOD, MACD_SLOW_PERIOD,
                                                      MACD_SIGNAL_PERIOD)) and \
            TechnicalIndicators.analyze_volume_change(stock_data) == "成交量显著增加" and \
            TechnicalIndicators.calculate_price_volatility(stock_data) < 0.12 and \
            (stock_data['close'].iloc[-1] < stock_data['bollinger_upper'].iloc[-1] and
             stock_data['close'].iloc[-1] > stock_data['bollinger_middle'].iloc[-1]) and \
            TechnicalIndicators.detect_kdj_crossover(stock_data) and has_gene:
        logging.info(f"{symbol} 是较好的买点且有涨停基因，原因: {', '.join(reasons)}")
        return "较好的买点且有涨停基因", reasons
    elif ((stock_data['sma_short'].iloc[-1] > stock_data['sma_long'].iloc[-1] and stock_data['sma_short'].iloc[-2] <=
            stock_data['sma_long'].iloc[-2]) and \
          (TechnicalIndicators.detect_rsi_oversold(stock_data, RSI_PERIOD, OVERSOLD_THRESHOLD) or
           TechnicalIndicators.detect_macd_crossover(stock_data, MACD_FAST_PERIOD, MACD_SLOW_PERIOD,
                                                    MACD_SIGNAL_PERIOD)) and \
          TechnicalIndicators.analyze_volume_change(stock_data) == "成交量显著增加" and \
          TechnicalIndicators.calculate_price_volatility(stock_data) < 0.12) or \
            ((stock_data['close'].iloc[-1] < stock_data['bollinger_upper'].iloc[-1] and
              stock_data['close'].iloc[-1] > stock_data['bollinger_middle'].iloc[-1]) and \
             TechnicalIndicators.detect_kdj_crossover(stock_data) and has_gene):
        logging.info(f"{symbol} 是潜在买点且有涨停基因，原因: {', '.join(reasons)}")
        return "潜在买点且有涨停基因", reasons
    elif has_gene:
        logging.info(f"{symbol} 有涨停基因，但买点不明确，原因: {', '.join(reasons)}")
        return "有涨停基因，但买点不明确", reasons
    logging.info(f"{symbol} 目前未发现明显买点信号，原因: {', '.join(reasons)}")
    return "目前未发现明显买点信号", reasons


def get_all_stock_codes() -> pd.DataFrame:
    logging.info("正在获取所有股票代码...")
    stock_info = ak.stock_info_a_code_name()

    def add_prefix(code: str) -> str:
        if code.startswith("300"):
            return "sz" + code
        elif code.startswith("60"):
            return "sh" + code
        elif code.startswith("00"):
            return "sz" + code
        return None

    stock_info["code_with_prefix"] = stock_info["code"].apply(add_prefix)
    filtered_stock_info = stock_info.dropna(subset=['code_with_prefix'])
    logging.info(f"完成获取所有股票代码，共 {len(filtered_stock_info)} 个。")
    return filtered_stock_info


def main():
    # all_stock_codes = get_all_stock_codes()
    all_stock_codes =['sz000001']
    results = []
    for code in all_stock_codes:
        logging.info(f"正在处理股票: {code}")
        category, reasons = predict_buy_point(code)
        results.append({'股票代码': code, '分类': category, '原因': ', '.join(reasons)})

    results_df = pd.DataFrame(results)

if __name__ == "__main__":
    main()